package com.sghd.fight.module.game.core.model;

import com.sghd.common.utils.ManagedException;
import com.sghd.fight.module.game.config.ConfigNames;
import com.sghd.fight.module.game.config.ElementConfig;
import com.sghd.fight.module.game.config.ElementPk;
import com.sghd.fight.module.game.config.SkillConfig;
import com.sghd.fight.module.game.core.Magic;
import com.sghd.fight.module.game.service.room.IRoom;
import com.sghd.fight.module.game.service.room.Player;
import com.sghd.fight.module.game.service.skill.Context;
import com.sghd.fight.module.game.service.skill.effect.Effect;
import com.sghd.fight.module.game.service.work.RoomRunner;
import com.sghd.pojo.fight.game.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * @author frank
 * @date 2018-10-22 16:15
 * @desc 游戏单位
 **/
public class Unit {
    private Logger logger = LoggerFactory.getLogger(getClass());
    protected long id;
    protected String name;
    /** 状态值 */
    protected int state = UnitState.NORMAL;
    /**所属战斗*/
    protected IRoom room;
    /**攻击自己的敌方*/
    protected Unit source;
    /** 数值属性 */
    protected Map<UnitValue, Integer> values;
    /**buff状态,id递增排序*/
    protected TreeMap<Integer, Buff> buffs = new TreeMap<>((o1, o2) -> ((Integer) o1).compareTo(((Integer) o2)));
    /**类型*/
    protected UnitEnum type;

    protected boolean ready = false;
    /** 魔法值*/
    protected Magic magic;
    /** 剩余回会数*/
    protected int round = 0;

    public void addWork(RoomRunner runner, Date time) {
        room.addWork(runner, time);
    }

    /** 上阵的技能*/
    protected Map<Integer, BattleSkill> skills = new HashMap<>();

    public Unit(long id, String name, IRoom room) {
        this.id = id;
        this.name = name;
        this.room = room;
        this.magic = new Magic(room.getConfigService().getGameConfig(ConfigNames.MAGIC_MIN));
        values = UnitValue.getDefaults();
    }

    private long nextSeed = -1;
    protected boolean first = false;

    public void decrease() {
        round--;
    }

    public void increase() {
        round++;
    }

    /**
     * 角色死亡
     * */
    public void dead() {
        addState(UnitState.DEAD);
        if (null != source) {
            source.targetDead(this);
        }
        room.dead();
    }

    public void targetDead(Unit target) {

    }

    public int getSkillLevel(int skillId) {
        return 0;
    }

    public Map<Integer, BattleSkill> getSkills() {
        return skills;
    }

    /** 攻击者*/
    public void addSource(Unit source) {
        this.source = source;
    }

    /** 添加状态 */
    public void addState(int status) {
        state = state | status;
    }

    /** 移除状态 */
    public void removeState(int status) {
        if (hasState(status)) {
            state = state ^ status;
        }
    }

    /** 检查是否有某种状态 */
    public boolean hasState(int status) {
        return (state & status) == status;
    }

    /** 检查是否死亡 */
    public boolean isDead() {
        return hasState(UnitState.DEAD);
    }

    /**
     * 增加/减少属性值
     * @param type 约定的键名
     * @param value 增量
     * @return 最新值
     */
    public int increaseValue(UnitValue type, int value) {
        int current = getValue(type);
        switch (type) {
            case HP:
                if (current <= 0) {
                    break;
                }
                current += value;
                if (current <= 0) {
                    current = 0;
                    dead();
                } else if (current > getValue(UnitValue.HP_MAX)) {
                    current = getValue(UnitValue.HP_MAX);
                }
                setValue(type, current);
                break;
            case STATE:
                if (value > 0) {
                    addState(value);
                } else {
                    removeState(Math.abs(value));
                }
                break;
            default:
                current += value;
                if (current < 0) {
                    current = 0;
                }
                setValue(type, current);
                break;
        }
        return current;
    }

    /**
     *  技能消耗判断
     * @param skillConfig
     */
    public void consume(SkillConfig skillConfig) {
        if (this instanceof Player) {
            int skillLevel = ((Player) this).getSkillLevel(skillConfig.getId());
            Map<ChessType, Integer> expends = skillConfig.getExpends(skillLevel);
            if (expends != null && !expends.isEmpty()) {
                Map<ChessType, Integer> magics = magic.getMagics();
                for (ChessType type : expends.keySet()) {
                    Integer value = expends.get(type);
                    if (magics.get(type) < value) {
                        throw new ManagedException(-1, "魔法值不足！");
                    }
                }
                //消耗魔法值
                for (ChessType type : expends.keySet()) {
                    Integer value = expends.get(type);
                    magics.put(type, magics.get(type) - value);
                }
            }
        }
        //消耗回合数
        logger.debug("技能[{}]消耗的回合数[{}]", skillConfig.getId(), skillConfig.getRound());
        this.round -= skillConfig.getRound();
    }

    /**
     * 获取指定的数值属性
     * @param type 属性类型
     * @return
     */
    public int getValue(UnitValue type) {
        Integer ret = values.get(type);
        if (ret == null) {
            return 0;
        } else {
            return ret;
        }
    }

    /**
     * 获取指定的数值属性
     * @param type 属性类型
     * @return
     */
    public int getValue(String type) {
        return getValue(UnitValue.valueOf(type));
    }

    /**
     * 设置指定的数值属性
     * @param type 属性类型
     * @param value 值
     * @return
     */
    public void setValue(UnitValue type, int value) {
        values.put(type, value);
    }

    public void addBuff(Buff buff) {
        if (buff.getType() == UnitValue.MAGIC_SHIELD) {//同效果不叠加,移除原来的buff
            Collection<Integer> remove = new HashSet<>();
            for (Buff buff1 : buffs.values()) {
                if (buff1.getType() == UnitValue.MAGIC_SHIELD) {
                    remove.add(buff1.getId());
                }
            }
            removeBuff(remove);
        }
        int buffId = buff.getId();
        Buff prev = buffs.get(buffId);
        if (prev != null) {
            increaseValue(prev.getType(), -prev.getValue());
            prev.update(buff);
        } else {
            buffs.put(buffId, buff);
        }
        increaseValue(buff.getType(), buff.getValue());
    }

    public Buff getBuff(int id) {
        return buffs.get(id);
    }

    public Map<Integer, Buff> getBuffs() {
        return buffs;
    }

    public void removeBuff(int buffId) {
        Buff buff = buffs.remove(buffId);
        if (buff != null) {
            logger.debug("移除buff[{}]-[{}]", buffId, buff.getType());
            increaseValue(buff.getType(), -buff.getValue());
        }
    }

    public void removeBuff(Collection<Integer> buffIds) {
        for (Integer buffId : buffIds) {
            removeBuff(buffId);
            logger.debug("移除buff[{}]", buffId);
        }
    }

    /**
     * 执行技能
     * @param owner 施放者
     * @param room 房间
     * @param config 技能配置
     * @param skillCtx 技能上下文对象
     */
    public SkillReport execute(IRoom room, SkillConfig config, Map<String, Object> skillCtx) {
        Context context = new Context(this);
        List<Unit> targets = getTarget(this, room, config);
        int skillLevel = 0;
        if (this instanceof Player) {
            skillLevel = ((Player) this).getSkillLevel(config.getId());
        }
        SkillReport report = new SkillReport(id, config.getId(), config.getExpends(skillLevel), config.getRound());
        for (Unit target : targets) {
            if (target == null || target.isDead()) {
                continue;
            }
            TargetReport targetReport = new TargetReport(target.getId(), id);
//            Map<String, Object> targetCtx = new HashMap<>();
//            if (target.equals(this)) {
//                targetCtx.put(Effect.IS_SELF, true);
//            } else {
//                targetCtx.put(Effect.IS_SELF, false);
//            }
            Effect effect = config.getEffect();
            skillCtx.putAll(config.getContext());
            effect.execute(context, room, this, target, skillCtx, report, targetReport, config);
//            for (int i = 0; i < effects.length; i++) {
//                int effectId = effects[i];
//                EffectConfig effectConfig = SkillService.getEffect(effectId);
//                //效果初始上下文
//                Map<String, Object> effectCtx = toSkillEffectCtx(effectConfig, this, target);
//                effectCtx.putAll(skillCtx);
//                effectCtx.putAll(targetCtx);
//                Effect effect = SkillService.getEffectHandler(effectConfig.getType());
//                //计算数值
//            }
            report.addTarget(targetReport);
        }
        //定时器使上下文中的数值变化生效
//        SkillExecuteRunner runner = SkillExecuteRunner.valueOf(owner, context, config);
//        room.addWork(runner, new Date());//技能施放时间(生效)
        context.effect();//立即生效
        return report;
    }

    /** 构造技能效果公式上下文 */
//    public Map<String, Object> toSkillEffectCtx(EffectConfig config, Unit owner, Unit target) {
//        HashMap<String, Object> ctx = config.cloneCtx();
//        ctx.put(Effect.OWNER, owner);
//        ctx.put(Effect.TARGET, target);
//        return ctx;
//    }
    public List<Unit> getTarget(Unit owner, IRoom room, SkillConfig config) {
        Collection<Unit> units = room.getUnits().values();
        switch (config.getTarget()) {
            case Effect.TARGET_SEL:
                return Arrays.asList(owner);
            case Effect.TARGET_ENEMY:
                for (Unit unit : units) {
                    if (unit.equals(owner)) {
                        continue;
                    }
                    return Arrays.asList(unit);
                }
                break;
            case Effect.TARGET_ALL:
                List<Unit> results = new ArrayList<>();
                for (Unit unit : units) {
                    results.add(unit);
                }
                return results;
        }
        throw new RuntimeException("选择技能目标错误！");
    }

    /** 自定义行为*/
    public void action() {
    }

    public void roundBegin() {
        logger.debug("玩家[{}]回合开始！", id);
        beginBuff();//处理buff
        beginSkill();
        room.submit(now -> {
            action();
        });
    }

    public void roundEnd() {
        logger.debug("玩家[{}]回合结束！", id);
        endBuff();//处理buff
    }

    /**
     * 技能冷确
     */
    protected void beginSkill() {
        for (BattleSkill battleSkill : skills.values()) {
            battleSkill.cool();
        }
    }

    public BattleSkill getSkill(int skillId) {
        return skills.get(skillId);
    }

    /** 回合开始检需要移除buff处理,按buffId递增处理*/
    public void beginBuff() {
        Collection<Integer> remove = new HashSet<>();
        for (Map.Entry<Integer, Buff> entry : buffs.entrySet()) {
            Buff buff = entry.getValue();
            UnitValue buffType = buff.getType();
            boolean clean = false;
            switch (buffType) {
                case MAGIC_SHIELD:
                    //更新魔法值
                    buff.setMgice(this);
                    clean = buff.isRemove();
                    if (clean) {
                        logger.debug("回合开始，移除魔法盾！");
                        remove.add(buff.getId());
                    } else {
                        ChessType type = ChessType.valueOf(buff.getCtx().get(Effect.MAGIC_TYPE).toString());
                        Magic magic = ((Player) this).getMagic();
                        //更新魔法值,消耗两回合
                        magic.update(type, -buff.getCoolVal());
                    }
                    break;
                case ENWIND:
                    int enwindVal = (int) (buff.getValue() * getBet());
                    enwindVal = magic2Hp(this, enwindVal, remove);
                    //五行减伤
                    enwindVal = elementFix(buff.getSkillId(), enwindVal);
                    increaseValue(UnitValue.HP, -enwindVal);
                    buff.decrease(buff.getCoolVal());
                    logger.debug("缠绕掉血[{}],剩余[{}]", enwindVal, buff.getTime());
                    if (buff.isRemove()) {
                        remove.add(buff.getId());
                    }
                    break;
                case REJUVENATION:
                    //每回合回复2血
                    increaseValue(UnitValue.HP, buff.getValue());
                    buff.decrease(buff.getCoolVal());
                    logger.debug("回春术回复[{}]剩余[{}]", buff.getValue(), buff.getTime());
                    if (buff.isRemove()) {
                        remove.add(buff.getId());
                    }
                    break;
                case TOXIC:
                    int toxicVal = (int) (buff.getValue() * getBet());
                    toxicVal = magic2Hp(this, toxicVal, remove);
                    //玩家五行减伤
                    toxicVal = elementFix(buff.getSkillId(), toxicVal);
                    //每回合扣2血
                    increaseValue(UnitValue.HP, -toxicVal);
                    logger.debug("毒雾掉血[{}],剩余[{}]", toxicVal, buff.getTime());
                    //有封魔效果，回合结束才移除！
                    break;
                case WORRY:
                    int worryVal = (int) (buff.getValue() * getBet());
                    worryVal = magic2Hp(this, worryVal, remove);
                    //每回合扣1血
                    increaseValue(UnitValue.HP, -worryVal);
                    buff.decrease(buff.getCoolVal());
                    logger.debug("撕咬掉血[{}],剩余[{}]", worryVal, buff.getTime());
                    if (buff.isRemove()) {
                        remove.add(buff.getId());
                    }
                    break;
                case FIREBALL:
                    int fireballVal = (int) (buff.getValue() * getBet());
                    fireballVal = magic2Hp(this, fireballVal, remove);
                    //每回合扣1血
                    increaseValue(UnitValue.HP, -fireballVal);
                    buff.decrease(buff.getCoolVal());
                    logger.debug("连珠火球掉血[{}],剩余[{}]", fireballVal, buff.getTime());
                    if (buff.isRemove()) {
                        remove.add(buff.getId());
                    }
                    break;
                case CURSE:
                    //回合结束移除
                    break;
                case SEAL_MAGIC:
                    //回合结束移除
                    break;
                default:
                    buff.decrease(buff.getCoolVal());
                    clean = buff.isRemove();
                    if (clean) {
                        remove.add(buff.getId());
                    }
                    break;
            }
        }
        for (Integer buffId : remove) {
            removeBuff(buffId);
        }
    }

    //五行减伤
    private int elementFix(int skillId, int val) {
        SkillConfig skillConfig = room.getConfigService().getSkillConfig(skillId);
        //减伤
        if (this instanceof Player && skillConfig.getType() != null) {
            Map<ChessType, Integer> attr2Level = ((Player) this).getElement2Level();
            int level = attr2Level.get(skillConfig.getType());
            for (; level > 0; level--) {
                ElementPk pk = ElementPk.valueOf(skillConfig.getType(), level);
                ElementConfig attrConfig = room.getConfigService().getAttr(pk);
                if (logger.isDebugEnabled()) {
                    logger.debug("buff计算: 玩家[{}]五行额外减少伤害：{}", this.getId(), attrConfig.getAttr(room.getType()).getDeHarm());
                }
                //五行额外减攻
                val -= attrConfig.getAttr(room.getType()).getDeHarm();
            }
        }
        return val < 0 ? 0 : val;
    }

    private double getBet() {
        //自己是否有时间静止debuff
        double time_static = getValue(UnitValue.TIME_STATIC) / 100.0;
        if (time_static != UnitValue.TIME_STATIC.getValue() / 100.0) {
            //时间静止
            return time_static;
        }
        //敌方是否有魔法盾
        Unit target = getTarget();
        double magic_shield = target.getValue(UnitValue.MAGIC_SHIELD) / 100.0;
        if (magic_shield != UnitValue.MAGIC_SHIELD.getValue() / 100.0) {
            //魔法盾
            return magic_shield;
        }
        return 1.0;
    }

    private int magic2Hp(Unit unit, int val, Collection<Integer> remove) {
        //敌方有能量盾
        if (unit.hasBuf(UnitValue.ENERGY_SHIELD)) {
            logger.debug("敌方有能量盾：{}", val);
            val = unit.getValue(UnitValue.ENERGY_SHIELD);
        }
        if (unit.hasBuf(UnitValue.STONESKIN)) {
            logger.debug("敌方有石质皮肤：{}", val);
            val = val - unit.getValue(UnitValue.STONESKIN);
            if (val < 0) {
                val = 0;
            }
        }
        logger.debug("攻击造成伤害：{}", val);
        for (Buff buff : unit.getBuffs().values()) {
            if (buff.getType() == UnitValue.MAGIC_SHIELD) {//敌方魔法盾
                ChessType type = ChessType.valueOf(buff.getCtx().get(Effect.MAGIC_TYPE).toString());
                int magic = unit.getMagic(type);
                if (val >= magic) {
                    val = val - magic;
                } else {
                    magic = val;
                    val = 0;
                }
                logger.debug("当前魔法值 ：{}", unit.getMagic().getMagic(type));
                logger.debug("扣除的魔法值：{}", magic);
                unit.getMagic().update(type, -magic);//魔法值抵扣血量
                if (buff.isRemove()) {
                    remove.add(buff.getId());
                    logger.debug("[{}]魔法盾，被打破！", type);
                }
            }
        }
        logger.debug("最终造成的伤害：{}", val);
        return val;
    }

    private Unit getTarget() {
        for (Unit unit : room.getUnits().values()) {
            if (unit != this) {
                return unit;
            }
        }
        throw new RuntimeException("目标不存在");
    }

    /** 回合结束检需要移除buff处理buff*/
    public void endBuff() {
        Collection<Integer> remove = new HashSet<>();
        for (Map.Entry<Integer, Buff> entry : buffs.entrySet()) {
            Buff buff = entry.getValue();
            UnitValue buffType = buff.getType();
            switch (buffType) {//回合结束移除
                case SEAL_MAGIC:
                    buff.decrease(buff.getCoolVal());
                    if (buff.isRemove()) {
                        remove.add(buff.getId());
                    }
                    break;
                case CURSE://诅咒， 回合结束再移除buff
                    buff.decrease(buff.getCoolVal());
                    if (buff.isRemove()) {
                        remove.add(buff.getId());
                    }
                    break;
                case TOXIC://毒雾有封魔效果，回合结束再移除buff
                    buff.decrease(buff.getCoolVal());
                    if (buff.isRemove()) {
                        remove.add(buff.getId());
                    }
                    break;

            }
        }
        removeBuff(remove);
    }

    public boolean hasBuf(UnitValue type) {
        for (Buff buff : buffs.values()) {
            if (buff.getType() == type) {
                return true;
            }
        }
        return false;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getState() {
        return state;
    }

    public UnitEnum getType() {
        return type;
    }

    public void setType(UnitEnum type) {
        this.type = type;
    }

    public boolean getReady() {
        return ready;
    }

    public void setReady(boolean ready) {
        this.ready = ready;
    }

    public int getRound() {
        return round;
    }

    public void setRound(int round) {
        this.round = round;
    }

    public Magic getMagic() {
        return magic;
    }

    public void setMagic(Magic magic) {
        this.magic = magic;
    }

    public int getMagic(ChessType type) {
        return getMagic().getMagic(type);
    }

    public int getMagic(String type) {
        return getMagic().getMagic(ChessType.valueOf(type));
    }

    public void addStatus(int state) {
        if (hasStatus(state)) {
            return;
        }
        this.state |= state;
    }

    public boolean hasStatus(int state) {
        return (this.state & state) == state;
    }

    public void removeStatus(int state) {
        if (hasStatus(state)) {
            this.state ^= state;
        }
    }

    public UnitVo toVo() {
        UnitVo unitVo = new UnitVo(id, name);
        List<BattleSkillVo> skills = new ArrayList<>();
        for (BattleSkill battleSkill : this.skills.values()) {
            skills.add(battleSkill.toVo());
        }
        unitVo.setBattleSkills(skills);

        unitVo.setType(type.ordinal());
        return unitVo;
    }

    /***
     * debuff导致技能不可用
     * @return 是否无法使用技能
     */
    public boolean notUse() {
        return buffs.values().stream().anyMatch(
                buff -> buff.getType() == UnitValue.TOXIC || buff.getType() == UnitValue.SEAL_MAGIC);
    }

    public long getNextSeed() {
        return nextSeed;
    }

    public void setNextSeed(long nextSeed) {
        this.nextSeed = nextSeed;
    }

    public boolean isFirst() {
        return first;
    }

    public void setFirst(boolean first) {
        this.first = first;
    }

    public Map<UnitValue, Integer> getValues() {
        return values;
    }

    public void destory() {
        values = null;
        buffs = null;
        magic = null;
        skills = null;
        source = null;
    }
}
